#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#include<sys/wait.h>
#include<sys/types.h>
#include<fcntl.h>
#include<wait.h>

using namespace std;

#define COMMAND_SIZE 1024
#define MAXARGC 128
#define FORMAT "[%s@%s:%s]# "

//命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;

//环境变量表
#define MAX_ENVS 200
char* g_env[MAX_ENVS];
int g_envs = 0;

//exit code
int lastcode = 0;

//重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3



int redir = NONE_REDIR;
std::string filename;

const char* GetUserName()
{
    const char* name = getenv("USER");
    return name==NULL?"None":name;
}

const char* GetHostName()
{
    const char* hostname = getenv("HOSTNAME");
    return hostname==NULL?"None":hostname;
}

static char cwd[1024];
static char cwdenv[1024];

const char* GetPwd()
{
    const char* pwd = getcwd(cwd,sizeof(cwd));
    if(pwd!=NULL)
    {
        snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
        putenv(cwdenv);
    }
    return pwd==NULL?"None":pwd;
}

void MakeCommandLine(char com_prompt[],int size)
{
    snprintf(com_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}

void PrintCommandPrompt()
{
  char Prompt[COMMAND_SIZE];
  MakeCommandLine(Prompt,sizeof(Prompt));
  printf("%s",Prompt);
  fflush(stdout);
}

bool GetCommandLine(char* out,int size)
{
    //scanf 以空格作为分隔符不行
    //ls -a -l这是一个整体
    char *c = fgets(out,size,stdin);
    if(c==NULL) return false;
    out[strlen(out)-1]=0;//清理换行符

    if(strlen(out)==0) return false;

    return true;

}
//"ls -a -l"-> "ls" "-a" "-l"
bool CommandParse(char* commandline)
{
#define SEP " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline,SEP);
    while(g_argv[g_argc++]=strtok(NULL,SEP));
    g_argc--;

    return true;
}

void PrintfArgv()
{
    for(int i = 0;g_argv[i];i++)
    {
        printf("argv[%d]->%s\n",i,g_argv[i]);
    }

    printf("argc:%d\n",g_argc);
}

extern char**environ;

void InitEnv()
{
   // extern char**environ;
    memset(g_env,0,sizeof(g_env));

    g_envs = 0;

    //原本是要从配置文件中获取环境变量，但是这里就从environ中直接拿了
    for(int i = 0;environ[i];i++)
    {
        g_env[i] =(char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs] = (char*)malloc(strlen("TEST=HHHHHHHH")+1);//test
    strcpy(g_env[g_envs],"TEST=HHHHHHHH");
    g_envs++;
    g_env[g_envs] = NULL;

    //导成环境变量
   // for(int i = 0;g_env[i];i++)
   // {
   //     putenv(g_env[i]);
   // }

    environ = g_env;
}
static string prev_dir = "";

const char* GetHome()
{
    const char* home = getenv("HOME");
    return home==NULL?"None":home;
}

bool Cd()
{

    if(g_argc==1)
    {
        string home = GetHome();
        if(home.empty())
            return false;

        char cwd[1024];
        if(getcwd(cwd,sizeof(cwd))!=nullptr)
            prev_dir = cwd;

        if(chdir(home.c_str())!=0)
            return false;

    }
    else
    {
        string where = g_argv[1];
        if(where=="-")
        {
            if(prev_dir.empty())
                return false;
        }
        else if(where=="~")
        {

        }
        else
        {
            chdir(where.c_str());

        }
    }

    return true;
}

void Echo()
{

    if(g_argc==2)
    {
        string opt = g_argv[1];
        if(opt=="$?")
        {
            cout<<lastcode<<endl;
            lastcode = 0;
        }
        else if(opt[1]=='$')
        {
            string env_name = opt.substr(1);
            const char* env_value = getenv(env_name.c_str());
            if(env_value)
                cout<<env_value<<endl;
        }
        else
        {
            cout<<opt<<endl;
        }
    }
}


bool CheckBuiltIn()
{
    // int fd = -1;
    // if(redir==INPUT_REDIR)
    // {
    //     fd = open(filename.c_str(),O_RDONLY);
    //     if(fd<0)
    //         return true;
    //     prev = dup(0);
    //    dup2(fd,0);
    //    close(fd);
    // }
    // else if(redir==OUTPUT_REDIR)
    // {
    //     fd = open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC,0666);
    //     if(fd<0) return true;
    //     prev = dup(1);
    //     dup2(fd,1);
    //     close(fd);
    // }
    // else if(redir==APPEND_REDIR)
    // {
    // 
    //     fd = open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC,0666);
    //     if(fd<0) return true;
    //     prev = dup(1);
    //     dup2(fd,1);
    //     close(fd);
    // }
    // else
    // { }

    // execvp(g_argv[0],g_argv);
    string cmd = g_argv[0];
    if(cmd=="cd")
    {
        Cd();
        return true;
    }
    else if(cmd=="echo")
    {
        Echo();
        return true;
    }

    return false;
}

int Execute()
{
    pid_t id = fork();
    if(id==0)
    {
        //child
        int fd = -1;
        if(redir==INPUT_REDIR)
        {
            fd = open(filename.c_str(),O_RDONLY);
            if(fd<0)
                exit(1);
            dup2(fd,0);
            close(fd);
        }
        else if(redir==OUTPUT_REDIR)
        {
            fd = open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC,0666);
            if(fd<0) exit(2);
            dup2(fd,1);
            close(fd);
        }
        else if(redir==APPEND_REDIR)
        {
            
            fd = open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND,0666);
            if(fd<0) exit(3);
            dup2(fd,1);
            close(fd);
        }
        else
        {

        }

        execvp(g_argv[0],g_argv);
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id,&status,0);

    if(rid>0)
    {
        lastcode = WEXITSTATUS(status);
    }

    return 0;

}



void TrimSpace(char cmd[],int& end)
{
    while(isspace(cmd[end]))
    {
        end++;
    }

}

void RedirCheck(char cmd[])
{
    redir =  NONE_REDIR;
    filename.clear();
    int start = 0;
    int end = strlen(cmd)-1;
    
    while(end>start)
    {
        if(cmd[end]=='<')
        {
            cmd[end++] = '\0';
            TrimSpace(cmd,end);
            redir = INPUT_REDIR;
            filename = cmd+end;
            break;
        }
        else if(cmd[end]=='>')
        {
            if(cmd[end-1]=='>')
            {
                //>>
                cmd[end-1] = '\0';
                redir = APPEND_REDIR;
            }
            else
            {
                //>
                redir = OUTPUT_REDIR;

            }

            cmd[end++] = '\0';
            TrimSpace(cmd,end);
            filename = cmd+end;
            break;
        }
        else
        {
            end--;
        }
    }
    
}
int main()
{
    InitEnv();
    while(1)
    {
        //1.输出命令行提示符
        PrintCommandPrompt();

        //2.获取用户输入的命令
        char commandline[COMMAND_SIZE];
       if(!GetCommandLine(commandline,sizeof(commandline)))
           continue;

       // redir = NONE_REDIR;
       // filename.clear();
       //3.重定向分析
       RedirCheck(commandline);
        //4.命令行分析
        if(!CommandParse(commandline))
            continue;
        //PrintfArgv();

        //5.检查是否是内建命令
        if(CheckBuiltIn())
        {
            continue;
        }

        //6.执行命令
        Execute();

    
    }
    return 0;
}
